const pending = 'pending'
const fulfilled = 'fulfilled'
const rejected = 'rejected'

const execFuncCatchError = (execFunc, value, resolve, reject) => {
    try {
        const result = execFunc(value)
        resolve(result)
    } catch (error) {
        reject(error)
    }
}
class MyPromise {
    constructor(executor) {
        this.status = pending
        this.value = undefined
        this.reason = undefined

        this.onFulfilledFns = []
        this.onRejectedFns = []

        const resolve = (value) => {
            queueMicrotask(() => {
                if (this.status !== pending) return
                this.status = fulfilled
                this.value = value
                this.onFulfilledFns.forEach(fn => {
                    fn()
                })
            })

        }
        const reject = (reason) => {
            queueMicrotask(() => {
                if (this.status !== pending) return
                this.status = rejected
                this.reason = reason
                this.onRejectedFns.forEach(fn => {
                    fn()
                })
            })
        }
        try {
            executor(resolve, reject)
        } catch (error) {
            reject(error)
        }
    }
    then(onFulfilled = value => value, onRejected = err => { throw err }) {
        return new MyPromise((res, rej) => {
            // 1.状态fulfilled确定下来
            if (this.status === fulfilled) {
                execFuncCatchError(onFulfilled, this.value, res, rej)
            }
            // 2.状态rejected确定下来
            if (this.status === rejected) {
                execFuncCatchError(onRejected, this.reason, res, rej)
            }
            // 3.还不知道状态
            if (this.status === pending) {
                if (onFulfilled) {
                    this.onFulfilledFns.push(() => {
                        execFuncCatchError(onFulfilled, this.value, res, rej)
                    })
                }
                if (onRejected) {
                    this.onRejectedFns.push(() => {
                        execFuncCatchError(onRejected, this.reason, res, rej)
                    })
                }
            }
        })

    }
    catch(onRejected) {
        return this.then(undefined, onRejected)
    }
    finally(onFinally) {
        this.then(onFinally, onFinally)
    }
    static resolve(value) {
        return new MyPromise((resolve) => resolve(value))
    }
    static reject(reason) {
        return new MyPromise((undefined, reject) => reject(reason))
    }
    static all(promises) {
        return new MyPromise((resolve, reject) => {
            const values = []
            promises.forEach(promise => {
                promise.then((res) => {
                    values.push(res)
                    if (promises.length === values.length) {
                        resolve(values)
                    }
                }, (err) => {
                    reject(err)
                })
            });
        })
    }
    static allSettled(promises) {
        return new MyPromise((resolve) => {
            const values = []
            promises.forEach(promise => {
                promise.then(res => {
                    values.push({
                        status: fulfilled,
                        value: res
                    })
                }).catch(err => {
                    values.push({
                        status: rejected,
                        value: err
                    })
                }).finally(() => {
                    if (promises.length === values.length) {
                        resolve(values)
                    }
                })
            });
        })
    }
    static race(promises) {
        return new MyPromise((resolve, reject) => {
            promises.forEach((promise) => {
                promise.then(resolve, reject)
            })
        })
    }
    static any(promises) {
        return new MyPromise((resolve, reject) => {
            const reasons = []
            promises.forEach((promise) => {
                promise.then(resolve).catch(err => {
                    reasons.push(err)
                    if (promises.length === reasons.length) {
                        reject(reasons)
                    }
                })
            })
        })
    }

}
MyPromise.resolve(11).then(res => {
    console.log(res);
})

MyPromise.reject(22).catch(err => {
    console.log(err);
})
const p1 = new MyPromise((res, rej) => {
    setTimeout(() => {
        rej(111)
    }, 1000)
})
const p2 = new MyPromise((res, rej) => {
    setTimeout(() => {
        rej(222)
    }, 2000)
})
const p3 = new MyPromise((res, rej) => {
    setTimeout(() => {
        rej(333)
    }, 3000)
})
MyPromise.all([p1, p2, p3]).then(res => {
    console.log('all res ', res);
}).catch(err => {
    console.log('all err ', err);
})

MyPromise.allSettled([p1, p2, p3]).then(res => {
    console.log('allSettled res ', res);
}).catch(err => {
    console.log('allSettled err ', err);
})
MyPromise.race([p1, p2, p3]).then(res => {
    console.log('race res ', res);
}).catch(err => {
    console.log('race err ', err);
})

MyPromise.any([p1, p2, p3]).then(res => {
    console.log('any res ', res);
}).catch(err => {
    console.log('any err ', err);
})